import { useEffect, useLayoutEffect, useMemo, useRef } from "react";

import * as THREE from "three";

import { OrbitControls, useGLTF, useTexture, useCubeCamera } from "@react-three/drei";

import { useThree, useFrame } from "@react-three/fiber";

import { type GLTF } from "three-stdlib";

import CustomShaderMaterial from "three-custom-shader-material/vanilla";

// gsap的react的钩子
import { useGSAP } from "@gsap/react";
import gsap from "gsap";

import speedup_frag from "../shader/speedup.frag.glsl";
import speedup_vert from "../shader/speedup.vert.glsl";

import { flatModel, printModel } from "../../../util/index";
import { useGameStore, useInteractStore } from "../../../util/store";

import FloorParticle from "./floor_particle";

function CarContainer() {
  // 用于访问 Three.js 渲染器、场景和相机等核心对象
  const three = useThree();

  // 车身颜色
  const bodyColor = useGameStore((state) => state.bodyColor);

  // 用户交互   是否点击
  const touch = useInteractStore((state) => state.touch);

  // 加载车辆模型
  // 第二个参数是否缓存加载
  // 第三个参数是否异步加载
  const car_gltf = useGLTF("/model/sm_car.gltf", false, true);

  // 车辆纹理贴图
  const car_aoMap = useTexture("/textures/t_car_body_AO.raw.jpg");
  // flipY  纹理是否翻转
  car_aoMap.flipY = false;
  car_aoMap.colorSpace = THREE.LinearSRGBColorSpace;
  // minFilter和magFilter用户控制纹理的缩小和放大时的采样方式
  car_aoMap.minFilter = THREE.NearestFilter;
  car_aoMap.magFilter = THREE.NearestFilter;
  // channel  通道  0表示红色通道 1表示绿色通道 2表示蓝色通道 渲染出来的效果更加偏绿色
  car_aoMap.channel = 1;

  // 加载环境模型
  const environment_gltf = useGLTF("/model/sm_startroom.raw.gltf", false, true);

  // 地板粗糙度贴图
  const floor_roughness_map = useTexture("/textures/t_floor_roughness.webp");
  // 设置颜色空间 线性空间
  floor_roughness_map.colorSpace = THREE.LinearSRGBColorSpace;
  // 设置贴图纹理可以重复
  floor_roughness_map.wrapS = floor_roughness_map.wrapT = THREE.RepeatWrapping;

  // 地板法线贴图
  const floor_normal_map = useTexture("/textures/t_floor_normal.webp");
  floor_normal_map.colorSpace = THREE.LinearSRGBColorSpace;
  floor_normal_map.wrapS = floor_normal_map.wrapT = THREE.RepeatWrapping;

  // 地板环境光遮蔽贴图  aoMap  环境光遮蔽贴图
  const floor_aoMap = useTexture("/textures/t_startroom_ao.raw.jpg");
  floor_aoMap.flipY = false;
  // SRGBColorSpace  sRGB色彩空间 标准的RGB色彩空间
  floor_aoMap.colorSpace = THREE.SRGBColorSpace;
  floor_aoMap.minFilter = THREE.NearestFilter;
  floor_aoMap.magFilter = THREE.NearestFilter;
  floor_aoMap.channel = 1;

  // 地板光照贴图
  const floor_light_map = useTexture("/textures/t_startroom_light.raw.jpg");
  floor_light_map.flipY = false;
  floor_light_map.colorSpace = THREE.SRGBColorSpace;
  floor_light_map.minFilter = THREE.NearestFilter;
  floor_light_map.magFilter = THREE.NearestFilter;
  floor_light_map.channel = 1;

  // 加载隧道模型
  const speedup_gltf = useGLTF("/model/sm_speedup.gltf", false, true);

  const modelRef = useRef({
    wheel: [] as THREE.Mesh[],
    bodyMaterial: {} as THREE.MeshStandardMaterial | null,
    floor: null as THREE.Mesh | null,
    lightMaterial: {} as THREE.MeshPhysicalMaterial | null,
  });

  const params = useRef({
    speedFactor: 0, // 速度因子
    initColor: new THREE.Color("#fff"), // 初始颜色
    speedupColor: new THREE.Color("#000"), // 加速颜色
    floorColor: new THREE.Color("#fff"), // 地板颜色
    floorNormalSpeed: 0, // 地板法线速度
    bloomIntensity: 1, // 泛光强度
    bloomThreshold: 0.5, // 泛光阈值
    lightOpacity: 0.5, // 灯光透明度
    floorEnvIntensity: 0, // 地板环境光强度
    wheelRoughness: 1, // 轮胎粗糙度
    wheelEnvIntensity: 5, // 轮胎环境光强度
  });

  const handleModel = () => {
    car_model_handle();
    environment_model_handle();
  };
  // 车辆模型处理
  const car_model_handle = () => {
    // 拿到模型中的所有的mesh
    const car_model_arr = flatModel(car_gltf);
    console.log("car_model_arr");
    // printModel(car_model_arr);
    const car_body = car_model_arr.find((item) => item.name === "body") as THREE.Mesh;
    // 取出车身的材质
    const car_body_material = car_body.material as THREE.MeshStandardMaterial;
    // 全局光照影响的强度   环境贴图的强度    环境贴图用于模拟反射和环境光照的效果
    car_body_material.envMapIntensity = 5;
    // car_body_material.color = new THREE.Color('#26d6e9');
    // 纹理贴图
    car_model_arr.forEach((item: THREE.Mesh) => {
      if (item.isMesh) {
        const material = item.material as THREE.MeshStandardMaterial;
        material.aoMap = car_aoMap;
      }
    });
    const car_wheel = car_model_arr.find((item) => item.name === "Wheel") as THREE.Mesh;
    car_wheel.children.forEach((item) => {
      const mesh = item as THREE.Mesh;
      const material = mesh.material as THREE.MeshStandardMaterial;
      material.envMapIntensity = 5;
      modelRef.current.wheel.push(mesh);
    });
    modelRef.current.bodyMaterial = car_body_material;
  };
  // 环境模型处理
  const environment_model_handle = () => {
    const environment_model_arr = flatModel(environment_gltf);
    // console.log('environment_model_arr');
    // printModel(environment_model_arr);

    // 上面的灯光
    const light = environment_model_arr.find((item) => item.name === "light001") as THREE.Mesh;
    // 取出灯光的材质
    const light_material = light.material as THREE.MeshPhysicalMaterial;
    // 设置灯光的颜色
    // emissive   自发光
    light_material.emissive = new THREE.Color("#fff");
    // toneMapped  色调映射
    light_material.toneMapped = true;
    // 设置灯光的透明度
    light_material.transparent = true;
    light_material.opacity = 0.5;
    light_material.side = THREE.DoubleSide;
    light_material.alphaTest = 0.01;

    // 地板
    const floor = environment_model_arr.find((item) => item.name === "ReflecFloor") as THREE.Mesh;
    const floor_material = floor.material as THREE.MeshPhysicalMaterial;
    // 设置地板的粗糙度   要进行贴图
    floor_material.roughnessMap = floor_roughness_map;
    // 设置地板的法线贴图 法线贴图用于模拟凹凸不平的表面 使得表面光照效果看起来更加真实
    floor_material.normalMap = floor_normal_map;
    // aoMap  环境光遮蔽贴图
    floor_material.aoMap = floor_aoMap;
    // 光照贴图 光照贴图用于模拟光照效果
    floor_material.lightMap = floor_light_map;
    // 环境贴图的强度 因为我们有很多方面的贴图 所以可以不受环境贴图的影响
    floor_material.envMapIntensity = 0;

    modelRef.current.floor = floor;
    modelRef.current.lightMaterial = light_material;
  };

  // 车身颜色交互
  // 参数1：回调函数
  // 参数2：依赖项 dependencies 一个依赖组
  useGSAP(
    () => {
      if (!modelRef.current.bodyMaterial!.color) return;
      const params = {
        color: new THREE.Color(modelRef.current.bodyMaterial!.color),
        targetColor: new THREE.Color(bodyColor),
      };

      gsap.to(params.color, {
        duration: 0.8,
        ease: "power2.inOut", //缓动
        r: params.targetColor.r,
        g: params.targetColor.g,
        b: params.targetColor.b,
        // 重新渲染下面车模型的设置
        onUpdate: () => {
          modelRef.current.bodyMaterial!.color.set(params.color);
        },
      });
    },
    { dependencies: [bodyColor] }
  );
  // 地板环境车轮颜色交互
  useGSAP(
    () => {
      const base_params = params.current;
      const light_material = modelRef.current.lightMaterial;
      const floor = modelRef.current.floor?.material as THREE.MeshPhysicalMaterial;
      const wheel = modelRef.current.wheel;
      // 按下
      if (touch) {
        // 时间线
        const time = gsap.timeline();
        // 关灯
        time.to(
          base_params,
          {
            duration: 1.5,
            ease: "power1.in",
            lightOpacity: 0,
            onUpdate: () => {
              light_material && (light_material.opacity = base_params.lightOpacity);
            },
          },
          1
        );
        // 轮子和速度
        time.to(
          base_params,
          {
            duration: 1.5,
            ease: "power1.in",
            speedFactor: 1,
            wheelRoughness: 0,
            wheelEnvIntensity: 20,
            onUpdate: () => {
              uniforms.u_speedFactor.value = base_params.speedFactor;
              wheel.forEach((item) => {
                const material = item.material as THREE.MeshStandardMaterial;
                material.roughness = base_params.wheelRoughness;
                material.envMapIntensity = base_params.wheelEnvIntensity;
              });
            },
          },
          2
        );
      } else {
        // 抬起
        const time = gsap.timeline();
        // 开灯
        time.to(
          base_params,
          {
            duration: 1.5,
            ease: "power1.inOut",
            lightOpacity: 0.5,
            onUpdate: () => {
              light_material && (light_material.opacity = base_params.lightOpacity);
            },
          },
          1
        );
        // 轮子和速度
        time.to(
          base_params,
          {
            duration: 1.5,
            ease: "power1.inOut",
            speedFactor: 0,
            wheelRoughness: 1,
            wheelEnvIntensity: 5,
            onUpdate: () => {
              uniforms.u_speedFactor.value = base_params.speedFactor;
              wheel.forEach((item) => {
                const material = item.material as THREE.MeshStandardMaterial;
                material.roughness = base_params.wheelRoughness;
                material.envMapIntensity = base_params.wheelEnvIntensity;
              });
            },
          },
          2
        );
      }
    },
    {
      dependencies: [touch],
    }
  );

  type Uniforms = {
    u_time: THREE.IUniform;
    u_speedFactor: THREE.IUniform;
  };

  const uniforms = useMemo(() => {
    return {
      u_time: new THREE.Uniform(0),
      u_speedFactor: new THREE.Uniform(0),
    };
  }, []);
  // 隧道模型处理
  const speedup_gltf_shader_handle = useMemo(() => {
    return new CustomShaderMaterial({
      baseMaterial: THREE.ShaderMaterial,
      uniforms,
      silent: false, // 警告报错日志打印
      transparent: true,
      depthWrite: false,
      vertexShader: speedup_vert,
      fragmentShader: speedup_frag,
    });
  }, []);
  const loadSpeedupModel = (gltf: GLTF, material: CustomShaderMaterial) => {
    gltf.scene.traverse((child: THREE.Object3D) => {
      if ((child as THREE.Mesh).isMesh) {
        const mesh = child as THREE.Mesh;
        mesh.material = material;
      }
    });
  };
  useLayoutEffect(() => {
    handleModel();
    // 加载隧道模型
    loadSpeedupModel(speedup_gltf, speedup_gltf_shader_handle);
    // 将相机收集到的纹理渲染
    three.scene.environment = fbo.texture;
  }, []);

  // 相机
  // 用这个相机  可以理解为把一个相机每帧得到的纹理渲染到场景上去
  // fbo 帧缓冲对象（Frame Buffer Object），用于存储渲染结果。
  const { camera, fbo } = useCubeCamera({
    resolution: 512, //分辨率
  });
  // 设置纹理类型  UnsignedByteType 表示无符号字节类型。它通常用于指定纹理或缓冲区的数据类型。
  fbo.texture.type = THREE.UnsignedByteType; //无符号字节类型
  fbo.texture.generateMipmaps = false; //生成mipmaps
  fbo.texture.minFilter = THREE.NearestFilter; //最近过滤
  fbo.texture.magFilter = THREE.NearestFilter; //最近过滤

  // useFrame 用于在每一帧渲染时执行自定义逻辑。它类似于 Three.js 中的 requestAnimationFrame
  // state  状态
  // delta 时间差
  useFrame((state, delta) => {
    // 流媒体动起来
    uniforms.u_time.value += delta;
    // 收集环境之前  需要把车的模型隐藏
    car_gltf.scene.visible = false;
    camera.update(state.gl, three.scene);
    car_gltf.scene.visible = true;

    // 轮子动起来
    modelRef.current.wheel.forEach((item) => {
      item.rotateZ(-delta * 20 * params.current.speedFactor);
    });
  });

  return (
    <>
      <OrbitControls
        onStart={() => {
          useInteractStore.setState({ touch: true });
        }}
        onEnd={() => {
          useInteractStore.setState({ touch: false });
        }}
        maxDistance={10000000}
        minDistance={1}
      ></OrbitControls>
      <color attach="background" args={["#000"]}></color>
      <primitive object={car_gltf.scene}></primitive>
      <primitive object={environment_gltf.scene}></primitive>
      <primitive object={speedup_gltf.scene}></primitive>
      <FloorParticle uniforms={uniforms}></FloorParticle>
    </>
  );
}

export default CarContainer;
